import {Category} from 'cascadium-ledger';
import {unifyCols} from '../unify-cols';

import decomposeVouchers from './decompose-vouchers';
import mergeCategory from './merge-category';
import recategorize from './recategorize';

const cols = {
  'curr.code':  {desc:'科目代码', width: 50, isFilterable: true},
  'curr.name':  {desc:'科目名称', width: 250, isFilterable: true},
  'opening.dr': {desc:'期初借方', width: 30, type:'number', isSortable: true},
  'opening.cr': {desc:'期初贷方', width: 30, type:'number', isSortable: true},
  'accrual.dr': {desc:'借方发生', width: 30, type:'number', isSortable: true},
  'accrual.cr': {desc:'贷方发生', width: 30, type:'number', isSortable: true},
  'closing.dr': {desc:'期末借方', width: 30, type:'number', isSortable: true},
  'closing.cr': {desc:'期末贷方', width: 30, type:'number', isSortable: true},
  'dest.name':  {desc:'对方科目', width: 250, isSortable: true},
  voucherID:    {desc:'凭证编号', width: 150, isSortable: true},
  desc:         {desc:'凭证信息', width: 350, isFilterable: true}
}

const procDetailed = (data, books) => {

  let list = unifyCols(data, {EntryCons: Category})
    .filter(({attr:{itemTypeName}}) => !['部门', '项目'].includes(itemTypeName))

  const {data: UNIVERSAL} = books.find(({name}) => name === 'UNIVERSAL');
  const {data: JOURNAL} = books.find(({name}) => name === 'JOURNAL');

  const {data:cashflowTable} = books.find(({name}) => name === 'CASHFLOW_TABLE');

  const {cols:cashflowRulesColsMain} = books.find(({name}) => name === 'CASHFLOW_RULES_MAIN');
  // const {cols:cashflowRulesColsAdditional} = books.find(({name}) => name === 'CASHFLOW_RULES_ADDITIONAL');

  // 创建明细科目表，并且按月份累加获得全年的期初期末。将这个明细科目表
  // 按照其所属的科目汇入总科目余额表

  const categoryDetail = list
    .grip('curr.code')
    .grap(([, grouped]) => Category.from(grouped, {key: 'attr.month'}));

  // 再按科目代码将科目余额表的Ident汇入序时账。注意这里形式看起来
  // 像是把序时账汇入余额表，但这里其实仅是一个匹配的过程，在method
  // 中还是将科目表的Ident写入对应序时账的条目中。因为UNIVERSAL中
  // 的所有科目条目的curr都已经完成了setRef，所以这一步是将经过
  // setRef的curr赋值给voucherEntries中的条目。

  // 将序时账进行decompose操作，拆分后按照发生科目和对方科目重新
  // 分类。此处我们基于JOURNAL表中每个凭证中的分录创建了新的分录，
  // 然后在基于这些新的分录进行decompose，这样可以使得原有的序时
  // 账不受影响。
  // 
  // 需要注意的是，在很多账套中的序时账开头部分，有一系列的结转和
  // 调整分录，它们被用于计算一年的期初余额，因此没有明确的会计期
  // 间和凭证号，并且借方和贷方发生也并不相等，因此我们不认为它属
  // 于序时账所要处理的会计期间。它会陈列在序时账中，但是不会参与
  // 后续现流表的计算。

  if (UNIVERSAL.attr.BALANCE_UPDATED) {
    mergeCategory(UNIVERSAL, categoryDetail);

    const cateCodeMap = UNIVERSAL.unnestTo().map(({curr:{code}}) => code);
    const journalCodeMap = JOURNAL.map(({succ}) => succ).flat().grip('curr.code').keys();

    for (let i = 0; i < journalCodeMap.length; i++) {
      if (!cateCodeMap.includes(journalCodeMap[i])) {
        // console.log(UNIVERSAL);
        (new Category({
          code: journalCodeMap[i],
          name: '不存在的科目',
          attr: {nonExist: true},
        })).move(UNIVERSAL, {pushSide:'TAIL'});
      }
    }

    // 然后将末级科目的curr写入序时账的curr和dest。因为序时账无论
    // 发生科目还是对方科目都来自于末级科目。

    decomposeVouchers(UNIVERSAL, JOURNAL);

    for (let i = 0; i < JOURNAL.length; i++) {
      const {succ: entries} = JOURNAL[i];

      for (let k = 0; k < entries.length; k++) {
        
        entries[k].attr.level = 'ENTRY';

        if (entries[k].curr.ref.attr.nonExist) {
          entries[k].desc = '序时账中引用了科目余额表中不存在的科目，故此项发生金额暂不计入报表，请查阅原始凭证后手工修改'
        }
      }

      const incomeTax = entries.find(({curr}) => curr.ref?.attr.rule?.accord);

      if (incomeTax) {
        console.log('TAX', incomeTax);
        const [accordingCate, dir] = incomeTax.curr.ref.attr.rule?.accord;

        const accordance = entries.find(({curr}) => curr.ref?.attr.rule.alias === accordingCate);

        if (accordance) {
          console.log('TAX AGAINST', accordance);
        }
      }
    }

    recategorize(UNIVERSAL, JOURNAL);
  }

  UNIVERSAL.attr.INTEGRATED = true;

  // 以下部分用于初始化分配规则的编辑功能。当手动添加新规则时，需
  // 要从当前账套的科目设置中获取科目，这需要一个自动补全功能，而
  // 下面的方法提供了自动补全的路径，它和分配至现流表的过程没有关
  // 系。

  const paths = Object.fromEntries(UNIVERSAL.unnestTo()
    .filter(({attr:{level}}) => level === 'CATEGORY')
    .map(({path, succ}) => [path, succ.map(({curr}) => curr.name)]))

  paths[''] = UNIVERSAL.succ.map(({curr}) => curr.name);

  cashflowRulesColsMain.name.attr = {options: paths};
  // cashflowRulesColsAdditional.name.attr = {options: paths};

  const tableEntryMain = {
    '': cashflowTable.unnestTo()
        .filter(({curr:{code}, succ}) => succ.length === 0 && code.match(/[1-6]001.*/))
        .map(({curr:{name}}) => name)
  };

  cashflowRulesColsMain.dr.attr = {options: tableEntryMain};
  cashflowRulesColsMain.cr.attr = {options: tableEntryMain};

  // const tableEntryAdditional = {
  //   '': cashflowTable.unnestTo()
  //       .filter(({curr:{code}, succ}) => succ.length === 0 && code.startsWith('8'))
  //       .map(({curr:{name}}) => name)
  // };

  // cashflowRulesColsAdditional.dr.attr = {options: tableEntryAdditional};
  // cashflowRulesColsAdditional.cr.attr = {options: tableEntryAdditional};

  return UNIVERSAL;

}

const procBalance = (data, books) => {

  const categoryEntries = unifyCols(data, {EntryCons:Category})
    .grip('curr.code')
    .grap(([, grouped]) => Category.from(grouped, {key: 'attr.month'}));

  for (let i = 0; i < categoryEntries.length; i++) {
    categoryEntries[i].attr.style = 'balance';
  }

  const category = new Category({code: '', name: '*'})
    .nestFrom(categoryEntries, 'curr.code');
  
  category.trav(({succ}) => {
      succ.orderBy('curr.code')
    }, 'POST');

  category.attr.BALANCE_UPDATED = true;
  return category;
}

const importProc = (name, data, books) => {

  const [sheet] = Object.values(data);

  switch(name) {
    case 'BALANCE':
      return procBalance(sheet, books);
    case 'DETAILED_BALANCE':
      return procDetailed(sheet, books);
  }
}

const exportProc = (name, books) => {

  const {data} = books.find(({name}) => name === 'UNIVERSAL');
  const UniversalDetailed = [];

  const levelAnnot = (level) => {
    return {
      CATEGORY: '科目',
      END_CATE: '末级科目',
      DEST_CATE: '按对方科目汇总',
    }[level];
  }

  const mapRec = ({curr, opening, closing, accrual, dest, attr}) => {
    return {
      
      发生科目: curr.name,
      科目编码: curr.code,
      所属一级科目: curr.ref?.path.split('/')[0],
      期初借方: opening?.dr,
      期初贷方: opening?.cr,
      借方发生: accrual.dr,
      贷方发生: accrual.cr,
      期末借方: closing?.dr,
      期末贷方: closing?.cr,
      对方科目: dest.name,
      对方编码: dest.code,
      对方所属一级科目: dest.ref?.path.split('/')[0],
      层级: levelAnnot(attr.level)
    };
  }

  data.trav(rec => {
    if (['CATEGORY', 'END_CATE', 'DEST_CATE'].includes(rec.attr.level)) {
      try{
        UniversalDetailed.push(mapRec(rec));
      } catch {
        console.warn('REC中包含未识别属性', rec);
      }
    }
  })
  
  const sheets = {
    'UNIVERSAL-科目总表': UniversalDetailed,
  };

  return sheets;
}


export default {
  name: 'UNIVERSAL',
  desc: '总账透视',
  descAddi: 'Universal Ledger',
  cols,
  data: [],
  eval: {
    // next: []
    next: ['CASHFLOW_RULES_MAIN']
  },
  dataTool: {
    useImport: {
      dataSpecs: [
        {
          name: 'BALANCE',
          desc: '科目余额表'
        },
        {
          name: 'DETAILED_BALANCE',
          desc: '明细科目余额表'
        },
      ],
      proc: importProc,
    },
    useExport: {
      displayName: '导出科目总表Excel',
      proc: exportProc
    }
  },
}
